#if FILE_TRACKER_ACTIVE
//#define _WIN32_WINNT 0x0500

#include "file_tracker.h"
#include "tracked.h"

#include "num/crc32.h"
#include "util/mmgr.h"					// Needs to come in early
#include "util/common_macros.h"		    // deletion, etc
#include "util/log.h"		    // deletion, etc

#include <windows.h>
#include <process.h>
#include <cassert>

using namespace res;

FileTracker& FileTracker::Instance() 
{
	static FileTracker Inst;
	return Inst;
}

FileTracker::FileTracker() :
	m_IsActive(false),
	m_WorkerThread(0)
{
	if (m_IsActive)
	{
		// init mutex
		InitializeCriticalSection(&m_CriticalSection);
		DWORD	Handle;
		// allow it to grow it's own stack
		m_WorkerThread = CreateThread (
					NULL,			// security
					1024*1024,		// stack size (0 = auto)
					WorkerThread,  this,	// thread and argument
					0,		// creation flags
					&Handle);
		assert(m_WorkerThread && "Thread not created!");

	}
}

FileTracker::~FileTracker()
{
	if (m_WorkerThread)
	{

		// die, die, die
		// would be better to use ExitThread, but this will hang 
		// the worker thread (which is waiting for stuff
		TerminateThread(m_WorkerThread, 0);

	}
	// clear mutex
	DeleteCriticalSection(&m_CriticalSection);

	// delete all the lists.
	for ( TrackMap::iterator It = m_TrackedFiles.begin( ) ; It != m_TrackedFiles.end( ) ; It++ )
	{
		UTIL_SAFE_DELETE(It->second);
	}
}


DWORD WINAPI  FileTracker::WorkerThread( void* Data )
{
	FileTracker* pTracker = (FileTracker*)Data;
	assert(pTracker && "Invalid tracker passed in!");
	// create handle
	HANDLE hDir = CreateFile( ".\\", // pointer to the file name
					FILE_LIST_DIRECTORY,                // access (read/write) mode
					FILE_SHARE_READ|FILE_SHARE_DELETE,  // share mode
					NULL,                               // security descriptor
					OPEN_EXISTING,                      // how to create
					FILE_FLAG_BACKUP_SEMANTICS,         // file attributes
					NULL                                // file with attributes to copy
				);

	// wait for notifications
	const int BUFFER_SIZE = 20;
	FILE_NOTIFY_INFORMATION Buffer[BUFFER_SIZE];
	memset(&Buffer, sizeof(Buffer), 0);
	DWORD BytesReturned;
	while( ReadDirectoryChangesW(
                                hDir,                               // handle to directory
                                &Buffer,                            // read results buffer
                                sizeof(Buffer),                     // length of buffer
                                TRUE,                               // monitoring option
                                FILE_NOTIFY_CHANGE_LAST_WRITE,		// looking for write only
                                &BytesReturned,						// bytes returned
                                NULL,								// overlapped buffer
                                NULL								// completion routine
                                ))
  {
		unsigned int i=0;	
		do
		{
			char	ConvertString[256];
			wcstombs(ConvertString, Buffer[i].FileName, sizeof(ConvertString));
			ConvertString[Buffer[i].FileNameLength/2] = '\0';	// terminate it.

			pTracker->AddChangedFile(ConvertString);
			i++;
		}
		while (!Buffer[i].NextEntryOffset);
		memset(&Buffer, sizeof(Buffer), 0);
		Sleep(1000);
	}
	return 0;
}

void FileTracker::AddChangedFile(const std::string& _File)
{
	// add mutex 
	EnterCriticalSection(&m_CriticalSection); 

	// push onto list of changed files
	m_ChangedFiles.push_back(_File);
	
	// clear mutex
	LeaveCriticalSection(&m_CriticalSection);
}

void FileTracker::Tick()
{
	// not tracking
	if (!m_IsActive) return;

	// check the list of files
	// test mutex
	EnterCriticalSection(&m_CriticalSection); 

	size_t Count = m_ChangedFiles.size();
	if (Count > 0)
	{
		for (unsigned int i=0;i<Count; i++)
		{
			FileUpdated(m_ChangedFiles[i]);
		}
		m_ChangedFiles.resize(0);
	}

	// clear mutex
	LeaveCriticalSection(&m_CriticalSection);
}

void FileTracker::StartTrackingFile(const std::string& _File, Tracked* _pUser)
{
	// not tracking
	if (!m_IsActive) return;


	unsigned int Key = GetFileID(_File);
	FileUsers*	pFileInfo = GetFileData(Key);
	if (pFileInfo)
	{
		// check to see if the user's already registered
		int	Index = pFileInfo->GetIndex(_pUser);
		if (Index == -1)
		{
			// not found registered to this file
			pFileInfo->m_Users.push_back(_pUser);
		}
	}
}

void FileTracker::EndTrackingFile(const std::string& _File, Tracked* _pUser)
{
	// not tracking
	if (!m_IsActive) return;

	unsigned int Key = GetFileID(_File);
	FileUsers*	pFileInfo = GetFileData(Key);
	if (pFileInfo)
	{
		// check to see if the user's already registered
		int	Index = pFileInfo->GetIndex(_pUser);
		if (Index != -1)
		{
			// found registered to this file... delete it
			pFileInfo->m_Users.erase(pFileInfo->m_Users.begin( ) + Index);
		}
	}
}

void FileTracker::FileUpdated(const std::string& _File)
{
	// should we sanitize the filename ?
	unsigned int ID = GetFileID(_File);
	// see if it's a valid file
	TrackMap::const_iterator It = m_TrackedFiles.find(ID);
	if(It == m_TrackedFiles.end())
		return;

	FileUsers*	pFileUser = It->second;
	if (pFileUser)
	{
		for (unsigned int i=0; i<pFileUser->m_Users.size(); i++)
		{
			Tracked* pTrack = pFileUser->m_Users[i];
			pTrack->OnFileChangeNotify(_File);
			_LOG(MSG_LOAD, "Reloading resource " << _File);
		}
	}
}

void FileTracker::RemoveTracked(Tracked* _pUser)
{
	// not tracking
	if (!m_IsActive) return;

	// expensive function.. shouldn't be used too often, I think.
	for ( TrackMap::iterator It = m_TrackedFiles.begin( ) ; It != m_TrackedFiles.end( ) ; It++ )
	{
		FileUsers* pUsers = It->second;
		int	Index = pUsers->GetIndex(_pUser);
		if (Index != -1)
		{
			// found registered to this file... delete it
			 pUsers->m_Users.erase(pUsers->m_Users.begin( ) + Index);
		}
	}
}

unsigned int FileTracker::GetFileID(const std::string& _File)
{
	// should we truncate the file name ?
	return num::CRC32::FastHash(_File);
}


FileTracker::FileUsers* FileTracker::GetFileData(unsigned long _Key)
{
	TrackMap::const_iterator It = m_TrackedFiles.find(_Key);
	// invalid ?
	if(It == m_TrackedFiles.end())
	{
		// create it and push it
		FileUsers* pUsers = new FileUsers;
		m_TrackedFiles.insert(std::make_pair<unsigned int, FileUsers*>(_Key, pUsers));
		return pUsers;
	} else
	{
		return It->second;
	}
	
}

int	FileTracker::FileUsers::GetIndex(Tracked* _pData)
{
	size_t Size = m_Users.size();
	for (size_t i = 0; i<Size; i++)
	{
		if (m_Users[i] == _pData)
			return (int)i;
	}	
	// not found
	return -1;
}
#endif